package com.lch.base.http;


import android.text.TextUtils;
import android.util.Log;

import com.lch.base.http.annotation.APIHeader;
import com.lch.base.http.annotation.APIIgnore;
import com.lch.base.http.annotation.APIMulti;
import com.lch.base.http.annotation.APIPath;
import com.lch.base.http.annotation.APIReq;
import com.lch.base.http.callback.ICallbackBase;
import com.lch.base.http.callback.RxBaseCallBack;
import com.lch.base.http.model.params.BaseParam;
import com.lch.base.http.request.RxRequestBody;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okio.BufferedSink;
import okio.Okio;

/**
 * 网络请求构建工具类
 *
 * @author lee(29487113@qq.com) on 18/2/4
 */
public class ParamUtils {
    public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");
    public static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");
    public static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
    public static final MediaType MEDIA_TYPE_JPG = MediaType.parse("image/jpg");
    public static final MediaType MEDIA_TYPE_TEXT = MediaType.parse("text/plain");


    private Object mBaseParam;
    private ICallbackBase mCallback;

    private Map<String, String> mPathMap = new HashMap<>();//替换带参数的url
    private Map<String, String> mHeaderMap = new HashMap<>(); //头部参数
    private MultipartBody mMultipartBody;
    /**
     * post body的请求参数 *
     */
    private TreeMap<String, Object> mReqMap = new TreeMap<>(new Comparator<Object>() {
        public int compare(Object o1, Object o2) {
            if (o1 == null || o2 == null) {
                return 0;
            } else {
                return String.valueOf(o1).compareTo(String.valueOf(o2));
            }
        }
    });


    public ParamUtils(Object baseParam) {
        mBaseParam = baseParam;
        parseFromObject();
    }

    public Map<String, String> getHeaderMap() {
        return mHeaderMap;
    }

    public Map<String, Object> getReqMap() {
        return mReqMap;
    }

    public Map<String, String> getPathMap() {
        return mPathMap;
    }

    public MultipartBody getMultipartBody(ICallbackBase callBack) {
        if (isMultiPart(mReqMap)) {
            mCallback = callBack;
            mMultipartBody = initMultipartBody(mReqMap);
        }
        return mMultipartBody;
    }

    //    public Map<String, Object> getMultiMap() {
//        return mMultiMap;
//    }


    private void parseFromObject() {
        if (mBaseParam != null) {
            parseFromObjectAsClass(mBaseParam.getClass());
        }
    }

    private void parseFromObjectAsClass(Class<?> clz) {
        if (null == clz) {
            return;
        }
        //判断Class1和Class2是否相同或是另一个类的子类或接口
        if (BaseParam.class.isAssignableFrom(clz)) {

            //getDeclaredFields()获得某个类的所有申明的字段，但是不包括父类的申明字段。
            //所以这里需要遍历父类的
            parseFromObjectAsClass(clz.getSuperclass());

            Field[] fs = clz.getDeclaredFields();
            for (int i = 0; i < fs.length; i++) {
                Field f = fs[i];
                f.setAccessible(true); //成员变量为private,故必须进行此操作才能访问
                try {
                    Object valueObject = f.get(mBaseParam); //获取对应字段的value值
                    if (valueObject != null && !"".equals(valueObject)) {
                        String name = f.getName();
                        String valueString = String.valueOf(valueObject);

                        if (f.isAnnotationPresent(APIPath.class)) {
                            String customName = f.getAnnotation(APIPath.class).name();
                            name = TextUtils.isEmpty(customName) ? name : customName;   //先判断是否有自定义名称
                            mPathMap.put(name, valueString);
                        }

                        else if (f.isAnnotationPresent(APIHeader.class)) {
                            String customName = f.getAnnotation(APIHeader.class).name();
                            name = TextUtils.isEmpty(customName) ? name : customName;   //先判断是否有自定义名称
                            try {
                                valueString = URLEncoder.encode(valueString, "UTF-8");    //OKHTTP的addHeader方法参数不支持中文 需要进行编码，编码方式可以外面设置
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            mHeaderMap.put(name, valueString);
                        }

                        else if (f.isAnnotationPresent(APIMulti.class)) {
                            String customName = f.getAnnotation(APIMulti.class).name();
                            name = TextUtils.isEmpty(customName) ? name : customName;  //先判断是否有自定义名称
                            mReqMap.put(name, valueObject); //保存整个对象
                        }

                        else if (f.isAnnotationPresent(APIReq.class)) {
                            String customName = f.getAnnotation(APIReq.class).name();
                            name = TextUtils.isEmpty(customName) ? name : customName;  //先判断是否有自定义名称
                            mReqMap.put(name, valueString);
                        }

                        else if (!f.isAnnotationPresent(APIIgnore.class)) {
                            //没有指定，默认跟APIReq一样，只是APIReq有自定义名字方法
                            mReqMap.put(name, valueString);
                        }
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 替换带参数的URL
     *
     * @param baseUrl eg：http://host.com/district/getProvinces/{countryId}
     * @return http://host.com/district/getProvinces/1/
     */
    public String genReqURL(String baseUrl) {
        String pathUrl = baseUrl;
        Iterator<?> iter = mPathMap.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<?, ?> entry = (Map.Entry<?, ?>) iter.next();
            Object key = entry.getKey();
            Object val = entry.getValue();

            try {
                if (pathUrl.contains("{" + key + "}")) {
                    pathUrl = pathUrl.replace("{" + key + "}", URLEncoder.encode(String.valueOf(val), "utf-8"));
                }
            } catch (UnsupportedEncodingException e) {

            }
        }

//        pathUrl += getReqURL(); //拼接了get参数

        return pathUrl;
    }

    /**
     * 获取get请求url
     */
    public String getReqURL() {
        String paramUrl = "";
        StringBuilder urlBuilder = new StringBuilder();
        Iterator<?> iter = mReqMap.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<?, ?> entry = (Map.Entry<?, ?>) iter.next();
            Object key = entry.getKey();
            Object val = entry.getValue();
            try {
                urlBuilder.append("&" + key + "=" + URLEncoder.encode(String.valueOf(val), "utf-8"));
            } catch (UnsupportedEncodingException e) {
                urlBuilder.append("&" + key + "=" + val);
            }
        }

        if (!urlBuilder.toString().equals("")) {
            paramUrl = urlBuilder.toString().replaceFirst("&", "?");

        }
        return paramUrl;
    }


    /**
     * 是否多媒体类型
     *
     * @param params
     * @return
     */
    public static boolean isMultiPart(Map<String, Object> params) {
        if (params != null) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                Object value = entry.getValue();
                if (value instanceof File || value instanceof byte[] || value instanceof InputStream)
                    return true;
            }
        }
        return false;
    }


    public void showMapLog(String mapName, Map map) {
        Log.i("lch2", "--------" + mapName + "-------------");
        Iterator<?> iter = map.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<?, ?> entry = (Map.Entry<?, ?>) iter.next();
            Object key = entry.getKey();
            Object val = entry.getValue();
            Log.i("lch2", key + ":" + val);
        }
        Log.i("lch2", "-----------end-------------");

    }

    /**
     * post提交的body，处理各种提交流
     * MultipartBody.Builder可以构建复杂的请求体, 与HTML文件上传形式兼容. 多块请求体中每块请求都是一个请求体,
     *
     * @param paramsMap
     */
    public MultipartBody initMultipartBody(final Map<String, Object> paramsMap) {
        //MultipartBody.Builder可以构建复杂的请求体, 与HTML文件上传形式兼容. 多块请求体中每块请求都是一个请求体,
        MultipartBody.Builder multipartBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);

        for (Map.Entry<String, Object> entry : paramsMap.entrySet()) {
            final Object obj = entry.getValue();
            if (obj == null) {
                continue;
            }

            String keyName = entry.getKey();

            //文件类型
            if (obj instanceof File) {
                File file = (File) obj;
                if (file.exists()) {
                    RxRequestBody requestBody = new RxRequestBody(RequestBody.create(getFileType(file), file), mCallback);
                    multipartBuilder.addFormDataPart(keyName, file.getName(), requestBody);
                } else {
                    Log.e("lch", "上传的文件不存在：" + file.getPath());
                }
            }

            //byte类型
            //BufferedSink.outputStream()来获取. OkHttp的底层对流和字节的操作都是基于Okio库, Okio库也是Square开发的另一个IO库, 填补I/O和NIO的空缺, 目的是提供简单便于使用的接口来操作IO.
            else if (obj instanceof byte[]) {

                multipartBuilder.addFormDataPart(keyName, entry.getKey(), new RequestBody() {
                    @Override
                    public MediaType contentType() {
                        return MEDIA_TYPE_MARKDOWN;
                    }

                    @Override
                    public void writeTo(BufferedSink sink) throws IOException {
                        sink.writeAll(Okio.source(new ByteArrayInputStream((byte[]) obj)));
                    }
                });
            }

            //InputStream类型
            else if (obj instanceof InputStream) {
                multipartBuilder.addFormDataPart(keyName, keyName, new RequestBody() {
                    @Override
                    public MediaType contentType() {
                        return MEDIA_TYPE_MARKDOWN;
                    }

                    @Override
                    public void writeTo(BufferedSink sink) throws IOException {
                        sink.writeAll(Okio.source((InputStream) obj));
                    }
                });
            } else {
                multipartBuilder.addFormDataPart(keyName, (String) obj);
            }
        }
        return multipartBuilder.build();
    }


    /**
     * 根据file不同后缀定义不同的type。其实这块后台不一定需要判断 只是做标准协议。可以跟后台协商
     *
     * @param file
     * @return
     */
    public static MediaType getFileType(File file) {
        String fileName = file.getName();
        int index = fileName.lastIndexOf(".");
        if (index > 0) {
            String prefix = fileName.substring(index + 1);
            if (prefix.equalsIgnoreCase("png")) {
                return MEDIA_TYPE_PNG;
            } else if (prefix.equalsIgnoreCase("jpg")) {
                return MEDIA_TYPE_JPG;
            }
        }
        return MEDIA_TYPE_MARKDOWN;

    }
}
